<?php
namespace Site\Controller;

use Site\Model\Point;
use Zend\Mvc\Controller\AbstractActionController,
    Zend\Mvc\MvcEvent,
    Zend\View\Model\ViewModel,
    Zend\View\Model\JsonModel,
    Site\Model\User,
    Zend\Validator\File\Size;

class SiteController extends AbstractActionController
{
    private $userTable, $pointTable, $_authentication, $pointTypeTable;

    /**
     * @param \Zend\Mvc\MvcEvent $event
     * @return void
     */
    public function onDispatch(MvcEvent $event)
    {

        if (!$this->validateSession() &&
            ($event->getRouteMatch()->getParam('action') !== 'sign-in') &&
            ($event->getRouteMatch()->getParam('action') !== 'sign-up') &&
            ($event->getRouteMatch()->getParam('action') !== 'sign-up-success') &&
            ($event->getRouteMatch()->getParam('action') !== 'index') &&
            ($event->getRouteMatch()->getParam('action') !== 'faq') &&
            ($event->getRouteMatch()->getParam('action') !== 'about-us') &&
            ($event->getRouteMatch()->getParam('action') !== 'save-point')&&
            ($event->getRouteMatch()->getParam('action') !== 'list-public-points')&&
            ($event->getRouteMatch()->getParam('action') !== 'point-profile')&&
            ($event->getRouteMatch()->getParam('action') !== 'get-public-points')&&
            ($event->getRouteMatch()->getParam('action') !== 'get-point-types')&&
            ($event->getRouteMatch()->getParam('action') !== 'about-us')&&
            ($event->getRouteMatch()->getParam('action') !== 'get-user-points')&&
            ($event->getRouteMatch()->getParam('action') !== 'save-new-user')&&
            ($event->getRouteMatch()->getParam('action') !== 'image-upload')
        ) {
            $this->redirect()->toRoute('site', array('action' => 'sign-in'));
        } else {
            $identity = $this->getAuthenticationService()->getStorage()->read();
            if ($identity) {
                $event->getViewModel()->setVariable('nick', isset($identity['nick']) ? $identity['nick'] : null);
            }
        }
        parent::onDispatch($event);
    }

    private function validateSession()
    {
        return $this->getAuthenticationService()->hasIdentity();
    }

    /**
     * @return \Site\Model\UserTable
     */
    public function getUserTable()
    {
        if (!$this->userTable) {
            $sm = $this->getServiceLocator();
            $this->userTable = $sm->get('Site\Model\UserTable');
        }
        return $this->userTable;
    }

    /**
     * @return \Site\Model\PointTable
     */
    public function getPointTable()
    {
        if (!$this->pointTable) {
            $sm = $this->getServiceLocator();
            $this->pointTable = $sm->get('Site\Model\PointTable');
        }
        return $this->pointTable;
    }

    /**
     * @return \Site\Model\PointTypeTable
     */
    public function getPointTypeTable()
    {
        if (!$this->pointTypeTable) {
            $sm = $this->getServiceLocator();
            $this->pointTypeTable = $sm->get('Site\Model\PointTypeTable');
        }
        return $this->pointTypeTable;
    }

    /**
     * @return \Zend\Authentication\AuthenticationService
     */
    protected function getAuthenticationService()
    {
        if (!$this->_authentication) {
            $this->_authentication = $this->getServiceLocator()->get('AuthService');
        }
        return $this->_authentication;
    }

    /************** calculate distance between two coordinates *******************************/

    private function coordDistance($lat1, $lng1, $lat2, $lng2)
    {
        $pi80 = M_PI / 180;
        $lat1 *= $pi80;
        $lng1 *= $pi80;
        $lat2 *= $pi80;
        $lng2 *= $pi80;

        $r = 6372.797; // mean radius of Earth in km
        $dlat = $lat2 - $lat1;
        $dlng = $lng2 - $lng1;
        $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlng / 2) * sin($dlng / 2);
        $c = 2 * atan2(sqrt($a), sqrt(1 - $a));
        $km = $r * $c;

        return $km;
    }


    public function indexAction()
    {
        $this->layout('layout/layout');
        return new ViewModel();
    }

    public function appHomeAction()
    {
        $this->layout('layout/layout-internal');
        return new ViewModel();
    }

    public function faqAction()
    {
        $this->layout('layout/layout');
        return new ViewModel();
    }

    public function aboutUsAction()
    {
        $this->layout('layout/layout');
        return new ViewModel();
    }

    public function dashboardAction()
    {
        $identity = $this->getAuthenticationService()->getStorage()->read();
        $publicPoints = $this->getPointTable()->getPointByNick($identity['nick']);
        $pointArray = array();
        foreach ($publicPoints as $point) {
            $pointArray[] = $point->toArray();
        }
        $viewModel = new ViewModel();
        $viewModel->setTerminal(true);
        $viewModel->setVariable('points', $pointArray);
        return $viewModel;
    }

    public function addPointAction()
    {

        if (!$this->request->isPost()) {
            if ($this->getRequest()->getQuery('lat')) {
                $lat = $this->getRequest()->getQuery('lat');
                $lng = $this->getRequest()->getQuery('lng');
                $elev = $this->getRequest()->getQuery('elev');
                $name = $this->getRequest()->getQuery('name');
                $newPoint = new Point();
                $newPoint->longitud = $lng;
                $newPoint->latitud = $lat;
                $newPoint->elevacion = $elev;
                $newPoint->espublico = ($this->getRequest()->getQuery('ispublic') == 'ispublic');
                $newPoint->nombre = $name;
                $newPoint->tipo = $this->getRequest()->getQuery('pointType');
                $newPoint->usuario = $this->getRequest()->getQuery('user');
                $newPoint->icono = $this->getRequest()->getQuery('image');
                $message = '';
                $error = 0;

                if (!$this->getPointTable()->savePoint($newPoint)) {
                    $error = 1;
                    $message = 'Error al almacenar punto.';
                }

                return new JsonModel(array(
                        'error' => $error,
                        'message' => $message
                    )
                );
            }
        }
        $viewModel = new ViewModel();
        $viewModel->setTerminal(true);
        $pointTypes = $this->getPointTypeTable()->fetchAll();
        $typeArray = array();
        foreach ($pointTypes as $type) {
            $typeArray[] = $type->toArray();
        }
        $viewModel->setVariable('pointTypes', $typeArray);
        return $viewModel;
    }

    public function imageUploadAction(){
        $filename = $this->request->getPost('filename');
        if(!$filename){
            return new JsonModel(array(
                    'error' => 0,
                    'message' => 'Falta el parámetro filename'
                )
            );
        }
        $user = $this->request->getPost('user');
        if(!$user){
            return new JsonModel(array(
                    'error' => 0,
                    'message' => 'Falta el parámetro user'
                )
            );
        }
        $content = $this->request->getPost('content');
        if(!$content){
            return new JsonModel(array(
                    'error' => 0,
                    'message' => 'Falta el parámetro content'
                )
            );
        }

        if($content && (strlen($content) > 1)){
            $fileContent = base64_decode($content);
            $filename = '/images/places/' .$user.'_place_'.$filename.'.jpg';
            if(!file_put_contents ('public'.$filename, $fileContent)){
                return new JsonModel(array(
                        'error' => 1,
                        'message' => 'No se pudo guardar imagen: '+$filename
                    )
                );
            }
            return new JsonModel(array(
                    'error' => 0,
                    'message' => $filename
                )
            );
        }
    }

    public function signInAction()
    {
        $viewModel = new ViewModel();

        if ($this->request->isPost()) {
            $user = $this->request->getPost('username');
            $pwd = $this->request->getPost('password');
            $this->getAuthenticationService()->clearIdentity();
            $this->getAuthenticationService()->getAdapter()
                ->setIdentity($user)
                ->setCredential($pwd);
            $result = $this->getAuthenticationService()->authenticate();

            if ($result->isValid()) {
                $userReg = $this->getUserTable()->getUserByNick($user);
                if ($userReg) {

                    $this->getAuthenticationService()->getStorage()->write($userReg->toArray());
                    return $this->redirect()->toRoute('site', array('action' => 'app-home'));
                }
            }
            $viewModel->setVariable('message', 'Usuario o pase incorrecto.');

        } else {
            if ($this->request->getQuery('source') == 'mobile') {
                $user = $this->request->getQuery('username');
                $pwd = $this->request->getQuery('password');
                $this->getAuthenticationService()->clearIdentity();
                $this->getAuthenticationService()->getAdapter()
                    ->setIdentity($user)
                    ->setCredential($pwd);
                $authResult = $this->getAuthenticationService()->authenticate();

                if ($authResult->isValid()) {
                    $userReg = $this->getUserTable()->getUserByNick($user);
                    if ($userReg) {

                        //$this->getAuthenticationService()->getStorage()->write($userReg->toArray());
                        $result = array(
                            'status' => 'OK',
                            'num_results' => 1,
                            'results' => $userReg->toArray()
                        );
                    }
                } else {
                    $result = array(
                        'status' => 'ERROR',
                        'num_results' => 1,
                        'results' => 'Usuario o pase incorrecto'
                    );
                }

                return new JsonModel($result);
            }
        }
        return $viewModel;

    }


    public function logOutAction()
    {
        $this->getAuthenticationService()->clearIdentity();
        return $this->redirect()->toRoute('site', array('action' => 'index'));
    }

    public function signUpAction()
    {
        $viewModel = new ViewModel();

        if ($this->request->isPost()) {
            $newUser = new User();
            $newUser->nick = $this->request->getPost('username');
            $userReg = $this->getUserTable()->getUserByNick($newUser->nick);
            if ($userReg) {
                $viewModel->setVariable('message', 'Usuario ya existe.');
                return $viewModel;
            }
            $newUser->pase = $this->request->getPost('password');
            $pass_confirm = $this->request->getPost('confirm_password');
            if (strcmp($newUser->pase, $pass_confirm) != 0) {
                $viewModel->setVariable('message', 'Pases no coinciden.');
                return $viewModel;
            }
            $newUser->nombre = $this->request->getPost('first_name');
            $newUser->apellido = $this->request->getPost('last_name');
            $newUser->correo = $this->request->getPost('email');
            $newUser->sitioweb = $this->request->getPost('website');

            if ($this->getUserTable()->saveUser($newUser))
                return $this->redirect()->toRoute('site', array('action' => 'index'));
            else {
                $viewModel->setVariable('message', 'Error al guardar el usuario. Intenta más tarde.');
                return $viewModel;
            }
        }
        return $viewModel;
    }



    public function listPublicPointsAction()
    {
        $this->layout('layout/layout-internal');
        $publicPoints = $this->getPointTable()->fetchAllPublic();
        $pointArray = array();
        foreach ($publicPoints as $point) {
            $pointArray[] = $point->toArray();
        }
        $viewModel = new ViewModel();
        $viewModel->setVariable('points', $pointArray);
        $viewModel->setTerminal(true);
        return $viewModel;
    }

    public function pointProfileAction()
    {
        $viewModel = new ViewModel();
        $source = $this->request->getQuery("source");
        if (strcmp($source, "mobile") === 0)
            $this->layout("layout/layout-mobile");
        $pointId = $this->getEvent()->getRouteMatch()->getParam('id');
        $result = $this->getPointTable()->getPointByID($pointId)->toArray();
        $viewModel->setVariable('point', $result[0]);
        $viewModel->setTerminal(false);
        return $viewModel;
    }


    /*
     * ******************************JSON ACTIONS************************************
     * */

    public function allUsersAction()
    {
        $result = array();
        $dataset = $this->getUserTable()->fetchAll();
        foreach ($dataset as $user) {
            $result[] = $user;
        }
        return new JsonModel($result);
    }

    public function UserAction()
    {
        $userId = $this->getEvent()->getRouteMatch()->getParam('id');
        $result = $this->getUserTable()->getUserByID($userId);


        if ($result)
            return new JsonModel($result->toArray());
        else
            return new JsonModel();
    }

    public function saveNewUserAction(){
        $newUser = new User();
        $newUser->nick = $this->request->getQuery('username');
        $userReg = $this->getUserTable()->getUserByNick($newUser->nick);
        if ($userReg) {
            return new JsonModel(array(
                    'error' => 1,
                    'message' => 'Usuario ya existe'
                )
            );
        }
        $newUser->pase = $this->request->getQuery('password');
        $pass_confirm = $this->request->getQuery('confirm_password');
        if (strcmp($newUser->pase, $pass_confirm) != 0) {

        }
        $newUser->nombre = $this->request->getQuery('first_name');
        $newUser->apellido = $this->request->getQuery('last_name');
        $newUser->correo = $this->request->getQuery('email');
        $newUser->sitioweb = $this->request->getQuery('website');

        if ($this->getUserTable()->saveUser($newUser)){
            $userObj = $this->getUserTable()->getUserByNick($newUser->nick);
            return new JsonModel(array(
                    'error' => 0,
                    'message' => $userObj->usuario
                )
            );
        }

        else {
            return new JsonModel(array(
                    'error' => 1,
                    'message' => 'Error al guardar el usuario. Intenta más tarde.'
                )
            );
        }
    }

    public function getPublicPointsAction()
    {
        $latReq = $this->request->getQuery('lat');
        $lngReq = $this->request->getQuery('lng');
        $radius = 0.0;
        if ($this->request->getQuery('radius')) {
            $radius = (double)$this->request->getQuery('radius');
        }
        $publicPoints = $this->getPointTable()->fetchAllPublic();
        $pointArray = array();
        foreach ($publicPoints as $point) {
            if ($radius && ($radius > 0)) {
                $point->distancia = $this->coordDistance($latReq, $lngReq, $point->latitud, $point->longitud);
                if ($point->distancia <= $radius)
                    $pointArray[] = $point->toMixareArray();
            } else {
                $pointArray[] = $point->toMixareArray();
            }
        }


        $result = array(
            'status' => 'OK',
            'num_results' => count($pointArray),
            'results' => $pointArray
        );
        return new JsonModel($result);
    }

    public function getPointTypesAction()
    {
        $pointTypes = $this->getPointTypeTable()->fetchAll();
        $typeArray = array();
        foreach ($pointTypes as $type) {
            $typeArray[] = $type->toArray();
        }

        $result = array(
            'status' => 'OK',
            'num_results' => count($typeArray),
            'results' => $typeArray
        );
        return new JsonModel($result);
    }

    public function getUserPointsAction()
    {
        $latReq = $this->request->getQuery('lat');
        $lngReq = $this->request->getQuery('lng');
        $radius = 0.0;
        if ($this->request->getQuery('radius')) {
            $radius = (double)$this->request->getQuery('radius');
        }
        $user = $this->request->getQuery('user');
        $publicPoints = $this->getPointTable()->getPointByNick($user);
        $pointArray = array();
        foreach ($publicPoints as $point) {
            if ($radius && ($radius > 0)) {
                $point->distancia = $this->coordDistance($latReq, $lngReq, $point->latitud, $point->longitud);
                if ($point->distancia <= $radius)
                    $pointArray[] = $point->toMixareArray();
            } else {
                $pointArray[] = $point->toMixareArray();
            }
        }


        $result = array(
            'status' => 'OK',
            'num_results' => count($pointArray),
            'results' => $pointArray
        );
        return new JsonModel($result);
    }


    public function savePointAction()
    {
        if ($this->request->isPost()) {
            if ($this->getRequest()->getPost('lat')) {
                $identity = $this->getAuthenticationService()->getStorage()->read();
                $lat = $this->getRequest()->getPost('lat');
                $lng = $this->getRequest()->getPost('lng');
                $elev = $this->getRequest()->getPost('elev');
                $name = $this->getRequest()->getPost('name');
                $newPoint = new Point();
                $newPoint->longitud = $lng;
                $newPoint->latitud = $lat;
                $newPoint->elevacion = $elev;
                $newPoint->espublico = ($this->getRequest()->getPost('ispublic') == 'ispublic');
                $newPoint->nombre = $name;
                $newPoint->tipo = $this->getRequest()->getPost('pointType');
                $newPoint->usuario = $identity['usuario'];
                $message = '';
                $error = 0;
                $picFile = $this->request->getFiles('picture');
                $fileName = '/images/places/' . $picFile['name'];
                if (!copy($picFile['tmp_name'], 'public' . $fileName)) {
                    $error = 1;
                    $message = 'Error al almacenar punto.';
                } else {
                    $newPoint->icono = $fileName;
                    if (!$this->getPointTable()->savePoint($newPoint)) {
                        $error = 1;
                        $message = 'Error al almacenar punto.';
                    }
                }

                return new JsonModel(array(
                        'error' => $error,
                        'message' => $message
                    )
                );
            }
            return new JsonModel(array(
                    'error' => 1,
                    'message' => 'Falta parámetros.'
                )
            );
        }
    }


}
